using System;
using System.Drawing;

namespace NobleTech.Products.CreatureKingdom.Utils
{
    public struct RectangleD : IRectangleD
    {
        private PointD leftTop;
        private SizeD size;

        public RectangleD(PointD corner, SizeD size)
        {
            this.leftTop = corner;
            this.size = size;
            Normalise();
        }

        public RectangleD(PointD corner1, PointD corner2)
        {
            this.leftTop = corner1;
            this.size = corner2 - corner1;
            Normalise();
        }

        public RectangleD(PointD centre, double radius)
        {
            radius = Math.Abs(radius);
            this.leftTop = centre - new SizeD(radius, radius);
            this.size = new SizeD(radius * 2, radius * 2);
        }

        public static explicit operator RectangleF(RectangleD rect)
        {
            return new RectangleF((PointF)rect.LeftTop, (SizeF)rect.Size);
        }

        public static implicit operator RectangleD(RectangleF rect)
        {
            return new RectangleD(rect.Location, rect.Size);
        }

        public override string ToString()
        {
            return string.Format("{0}-{1}", LeftTop, RightBottom);
        }

        public override int GetHashCode()
        {
            return leftTop.GetHashCode() ^ size.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            if (obj is RectangleD)
                return this == (RectangleD)obj;
            return ((RectangleF)this).Equals(obj);
        }

        public static bool operator ==(RectangleD a, RectangleD b)
        {
            return a.leftTop == b.leftTop && a.size == b.size;
        }

        public static bool operator !=(RectangleD a, RectangleD b) { return !(a == b); }

        public bool IsEmpty { get { return false; } }

        public PointD LeftTop
        {
            get { return leftTop; }
            set { leftTop = value; }
        }

        public SizeD Size
        {
            get { return size; }
            set
            {
                size = value;
                Normalise();
            }
        }

        public double Left
        {
            get { return LeftTop.X; }
            set { leftTop.X = value; }
        }

        public double Top
        {
            get { return LeftTop.Y; }
            set { leftTop.Y = value; }
        }

        public double Right
        {
            get { return LeftTop.X + size.Width; }
            set { leftTop.X = value - size.Width; }
        }

        public double Bottom
        {
            get { return LeftTop.Y + size.Height; }
            set { leftTop.Y = value - size.Height; }
        }

        public double Width
        {
            get { return Size.Width; }
            set { size.Width = value; }
        }

        public double Height
        {
            get { return Size.Height; }
            set { size.Height = value; }
        }

        public PointD LeftBottom
        {
            get { return LeftTop + new SizeD(0, Size.Height); }
            set { LeftTop = value - new SizeD(0, Size.Height); }
        }

        public PointD RightTop
        {
            get { return LeftTop + new SizeD(Size.Width, 0); }
            set { LeftTop = value - new SizeD(Size.Width, 0); }
        }

        public PointD RightBottom
        {
            get { return LeftTop + Size; }
            set { LeftTop = value - Size; }
        }

        public PointD Centre
        {
            get { return LeftTop + Size / 2; }
            set { LeftTop = value - Size / 2; }
        }

        public PointD MiddleTop
        {
            get { return LeftTop + new SizeD(Size.Width / 2, 0); }
            set { LeftTop = value - new SizeD(Size.Width / 2, 0); }
        }

        public PointD LeftMiddle
        {
            get { return LeftTop + new SizeD(0, Size.Height / 2); }
            set { LeftTop = value - new SizeD(0, Size.Height / 2); }
        }

        private void Normalise()
        {
            if (size.Width < 0)
            {
                leftTop.X += size.Width;
                size.Width = -size.Width;
            }
            if (size.Height < 0)
            {
                leftTop.Y += size.Height;
                size.Height = -size.Height;
            }
        }

        public bool Contains(PointD pt)
        {
            return pt >= LeftTop && pt < RightBottom;
        }

        public bool Contains(IRectangleD rect)
        {
            return Contains(rect.LeftTop) && Contains(rect.RightBottom);
        }

        public IRectangleD Extend(double left, double top, double right, double bottom)
        {
            return Extend(new SizeD(left, top), new SizeD(right, bottom));
        }

        public IRectangleD Extend(double leftRight, double topBottom)
        {
            return Extend(new SizeD(topBottom, leftRight));
        }

        public IRectangleD Extend(double size)
        {
            return Extend(size, size);
        }

        public IRectangleD Extend(SizeD size)
        {
            return Extend(size, size);
        }

        public IRectangleD Extend(SizeD leftTop, SizeD rightBottom)
        {
            return new RectangleD(this.leftTop - leftTop, size + leftTop + rightBottom);
        }

        public IRectangleD ExtendAround(PointD pt)
        {
            return new RectangleD(PointD.Min(LeftTop, pt), PointD.Max(RightBottom, pt + Size * 1.00001));
        }

        public static IRectangleD Union(IRectangleD rc1, IRectangleD rc2)
        {
            return new RectangleD(PointD.Min(rc1.LeftTop, rc2.LeftTop),
                PointD.Max(rc1.RightBottom, rc2.RightBottom));
        }

        public static IRectangleD Intersection(IRectangleD rc1, IRectangleD rc2)
        {
            if (PointD.Max(rc1.LeftTop, rc2.LeftTop) >=
                PointD.Min(rc1.RightBottom, rc2.RightBottom))
            {
                return new EmptyRectangleD();
            }
            return new RectangleD(PointD.Max(rc1.LeftTop, rc2.LeftTop),
                PointD.Min(rc1.RightBottom, rc2.RightBottom));
        }
    }
}
